package com.sky.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sky.constant.MessageConstant;
import com.sky.context.BaseContext;
import com.sky.dto.OrdersRejectionDTO;
import com.sky.dto.OrdersConfirmDTO;
import com.sky.dto.OrdersPageQueryDTO;
import com.sky.dto.OrdersCancelDTO;
import com.sky.dto.OrdersPageQueryDTO;
import com.sky.dto.OrdersSubmitDTO;
import com.sky.entity.*;
import com.sky.exception.AddressBookBusinessException;
import com.sky.exception.OrderBusinessException;
import com.sky.mapper.*;
import com.sky.result.PageResult;
import com.sky.properties.ShopProperties;
import com.sky.service.OrderService;
import com.sky.utils.WeChatPayUtil;
import com.sky.vo.OrderStatisticsVO;
import com.sky.utils.HttpClientUtil;
import com.sky.utils.MapParamUtil;
import com.sky.vo.OrderSubmitVO;
import com.sky.vo.OrderVO;
import lombok.extern.slf4j.Slf4j;
import com.sky.vo.OrderVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
    // 逆定理编码gc
    public static final String URL = "https://api.map.baidu.com/geocoding/v3";

    // 骑行地址
    public static final String Riding_Address_Url = "https://api.map.baidu.com/directionlite/v1/riding";

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private ShoppingCartMapper shoppingCartMapper;

    @Autowired
    private AddressBookMapper addressBookMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private WeChatPayUtil weChatPayUtil;

    @Autowired
    private ShopProperties shopProperties;

    /**
     * 用户提交订单
     * 1. 订单数据包含订单本身的基本数据和订单的详情数据【买了什么具体的东西】
     * 2. 它们是存储在不同的表里面
     * 订单的基本数据放置在 订单表 orders
     * 订单详情 放置在 订单详情表 order_detail
     * 3. 需要往这两张表添加数据
     * 3.1 先添加订单表 ， 获取到ID，再每一个订单详情里面设置它。
     * 3.2 再添加订单详情表
     *
     * @param dto
     * @return
     */
    @Override
    public OrderSubmitVO add(OrdersSubmitDTO dto) {
        //1. 往订单表添加数据

        //1.0 转化数据模型
        Orders orders = new Orders();
        BeanUtils.copyProperties(dto, orders);

        //1.1 准备数据
        //1.1.1 准备用户数据
        User user = userMapper.findById(BaseContext.getCurrentId());

        //1.1.2 准备地址数据
        AddressBook addressBook = addressBookMapper.getById(dto.getAddressBookId());

        //如果地址为空，则抛出异常！
        if(addressBook == null){
            throw new AddressBookBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
        }

        // 获取商家经纬度坐标map
        Map<String, Double> shopLngAndLatMap = getShopLngAndLatMap();
        // 获取用户地址经纬度坐标map
        Map<String, Double> UserLngAndLatMap = getUserLngAndLatMap(addressBook);

        // 校验配送距离是否超过5公里
        if (checkDistributionDistance(shopLngAndLatMap, UserLngAndLatMap)) {
            throw new AddressBookBusinessException(MessageConstant.ADDRESS_RANGE_EXCEEDS_5_KILOMETERS);
        }

        //1.2 补充数据
        fillOrders(orders, addressBook, user);

        //1.3 添加到订单表里面。
        orderMapper.add(orders);


        //2. 往订单详情表添加数据

        //2.1 先获取当前这个用户的购物车数据
        ShoppingCart cart = new ShoppingCart();
        cart.setUserId(BaseContext.getCurrentId());
        List<ShoppingCart> shoppingCartList = shoppingCartMapper.list(cart);

        //2.2 要往订单详情表【order_detail】存数据, 表有对应的实体类： OrderDetail, 所以我们需要把购物车转化成OrderDetail
        // 有多少个购物车对象就要转化出来多少给OrderDetail对象
        List<OrderDetail> orderDetailList = shoppingCartList.stream().map(shoppingCart -> {

            //2.2.1 遍历一次，构建一个新的OrderDetail对象
            OrderDetail od = new OrderDetail();

            //2.2.2 搬运数据
            BeanUtils.copyProperties(shoppingCart, od);

            //2.2.3 设置订单详情属于哪一个订单
            od.setOrderId(orders.getId());

            return od;
        }).collect(Collectors.toList());

        //2.3. 往订单详情表添加数据。
        orderDetailMapper.addAll(orderDetailList);


        //3. 清空购物车
        shoppingCartMapper.deleteByUid(BaseContext.getCurrentId());


        //4. 组建返回的结果：
        return OrderSubmitVO.builder()
                .id(orders.getId())
                .orderNumber(orders.getNumber())
                .orderAmount(orders.getAmount())
                .orderTime(orders.getOrderTime())
                .build();


        /*List<OrderDetail> orderDetailList = new ArrayList<>();
        for (ShoppingCart shoppingCart : shoppingCartList) {

            //2.2.1 遍历一次，就创建一个OrderDetail 对象
            OrderDetail od = new OrderDetail();

            //2.2.2 把购物车数据搬运到od 对象
            BeanUtils.copyProperties(shoppingCart ,od);

            //2.2.3 设置属于哪一个订单
            od.setOrderId(orders.getId());


            // 2.2.4 一边添加到订单详情表 【一个一个的添加...】
            //orderDetailMapper.add(od);

            //TODO:: 打包到一个集合去，一会批量添加。
            orderDetailList.add(od);
        }

        //TODO:: 在这里批量添加
        //orderDetailMapper.addAll(orderDetailList);*/
    }

    /**
     * 历史订单查询
     *
     * @param ordersPageQueryDTO
     */
    @Override
    public PageResult selectHistoryOrders(OrdersPageQueryDTO ordersPageQueryDTO) {
        Page<Object> p = PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());
        ordersPageQueryDTO.setUserId(BaseContext.getCurrentId());
        Page<Orders> ordersList = orderMapper.selectHistoryOrders(ordersPageQueryDTO);
        List<OrderVO> orderVOList = ordersList.stream().map(orders -> {
            OrderVO orderVO = new OrderVO();
            BeanUtils.copyProperties(orders, orderVO);
            List<OrderDetail> orderDetailList = orderMapper.selectOrderDetail(orders.getId());
            orderVO.setOrderDetailList(orderDetailList);
            return orderVO;
        }).collect(Collectors.toList());
        return new PageResult(p.getTotal(), orderVOList);
    }

    /**
     * 各个状态的订单数量统计
     *
     * @return
     */
    @Override
    public OrderStatisticsVO statistics() {
        OrderStatisticsVO orderStatisticsVO = new OrderStatisticsVO();
        int count1 = orderMapper.statistics(Orders.CONFIRMED);
        orderStatisticsVO.setConfirmed(count1);
        int count2 = orderMapper.statistics(Orders.DELIVERY_IN_PROGRESS);
        orderStatisticsVO.setDeliveryInProgress(count2);
        int count3 = orderMapper.statistics(Orders.TO_BE_CONFIRMED);
        orderStatisticsVO.setToBeConfirmed(count3);
        return orderStatisticsVO;
    }

    /**
     * 接单
     *
     * @param ordersConfirmDTO
     */
    @Override
    public void OrdersConfirm(OrdersConfirmDTO ordersConfirmDTO) {
        ordersConfirmDTO.setStatus(Orders.CONFIRMED);
        orderMapper.OrdersConfirm(ordersConfirmDTO);
    }

    /**
     * 完成订单
     *
     * @param id
     */
    @Override
    public void OrdersComplete(Long id) {
        Integer status = Orders.COMPLETED;
        orderMapper.OrdersComplete(id, status);
    }


    /**
     * 订单查询
     * @param id
     * @return
     */
    @Override
    public OrderVO details(Long id) {
        //通过主键id查到订单(数据库返回的是完整的orders表)
        Orders orders = orderMapper.getById(id);
        //通过orderid查到订单中所有菜品套餐的集合
        List<OrderDetail> orderDetails= orderDetailMapper.getByOrderId(orders.getId());
        //把集合放到订单表的属性中返回controller
        OrderVO orderVO = new OrderVO();
        BeanUtils.copyProperties(orders,orderVO);
        orderVO.setOrderDetailList(orderDetails);
        return orderVO;
    }

    /**
     * 取消订单
     * @param id
     */
    @Override
    public void userCancelById(Long id) throws Exception {
        //退款需要先把订单查出来
        Orders ordersFromDB = orderMapper.getById(id);

        //订单不存在抛异常
        if(ordersFromDB==null){
         throw new OrderBusinessException(MessageConstant.ORDER_NOT_FOUND);
        }
        //订单若状态大于2(非代付款or待接单)则抛异常
        if(ordersFromDB.getStatus()>2){
            throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
        }
//        创建一个新订单对象,赋主键值,!记录退款相关信息,最后在mapper更新
        Orders orders = new Orders();
        orders.setId(ordersFromDB.getId());
        //如果待接单状态是付过款的要退款(调用微信weChatPayUtil.refund退款接口)

        if(ordersFromDB.getStatus()==2){
            weChatPayUtil.refund(
                    /*订单号 退款单号 退款金额 原订单金额*/
                    ordersFromDB.getNumber(),
                    ordersFromDB.getNumber(),
                    /*?*/new BigDecimal(0.01),
                    new BigDecimal(0.01)
            );
            //(修改orders对象的PayStatus支付状态为退款)
            orders.setStatus(Orders.REFUND);
        }
        //else就是单纯取消订单 设置订单状态,取消原因,取消时间  最后在orderMapper更新
        orders.setStatus(Orders.CANCELLED);
        orders.setCancelReason("用户取消廖");
        orders.setCancelTime(LocalDateTime.now());

        //更新
        orderMapper.update(orders);

    }

    /**
     * 开盖有奖,再来一单
     * @param id
     */
    @Override
    public void repetition(Long id) {
        //根据threadlocal获取当前用户id
        Long userId = BaseContext.getCurrentId();
        //根据id获取当前订单详情对象的列表(?为何是集合)
        List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderId(userId);
        //通过stream流操作列表 将订单详情对象列表转化为购物车对象列表(再来一单)
        // stream的map中的匿名内部类需要返回值
        List<ShoppingCart> shoppingCartList = orderDetailList.stream().map(orderDetail -> {
            ShoppingCart shoppingCart = new ShoppingCart();
            //(将其中菜品信息重新赋值到购物车对象中 复制,id,time)
            BeanUtils.copyProperties(orderDetail, shoppingCart);
            shoppingCart.setId(BaseContext.getCurrentId());
            shoppingCart.setCreateTime(LocalDateTime.now());
            return shoppingCart;
        }).collect(Collectors.toList());
        //将购物车对象批量插入数据库中
//        orderDetailMapper.insertBatch(shoppingCartList); 不是往订单详情里加是往购物车加
        shoppingCartMapper.insertBatch(shoppingCartList);
    }

    /**
     * 拒绝订单
     * @param ordersRejectionDTO
     */
    @Override
    public void rejection(OrdersRejectionDTO ordersRejectionDTO) throws Exception {
        //根据订单拒绝dto的id获得订单
        Orders orderFromDB = orderMapper.getById(ordersRejectionDTO.getId());
        //只有状态值为2(待接单)才可以拒绝不然抛异常 或 订单为空也往外抛
        if(orderFromDB==null || !orderFromDB.getStatus().equals(Orders.TO_BE_CONFIRMED)){
            throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
        }
        //判断该订单支付状态 若已支付需要调用微信接口退款
        if(orderFromDB.getStatus()==Orders.PAID){
            String refund = weChatPayUtil.refund(
                    orderFromDB.getNumber(),
                    orderFromDB.getNumber(),
                    new BigDecimal(0.01),
                    new BigDecimal(0.01)
            );
            log.info("退款申请:{}",refund);
        }
        //new一个要放到数据库里的order更新id,订单状态,拒单原因,取消时间
        Orders orders = new Orders();
        orders.setId(orderFromDB.getId());
        orders.setStatus(Orders.CANCELLED);
        orders.setCancelReason(ordersRejectionDTO.getRejectionReason());
        orders.setCancelTime(LocalDateTime.now());
        //order表里更新
        orderMapper.update(orders);
    }

    /**
     * 填充订单数据
     * @param orders
     * @param addressBook
     * @param user
     */
    private void fillOrders(Orders orders , AddressBook addressBook , User user){
        //1.2.1 设置id :: 自增ID，不用设置
        //1.2.2 设置number 订单号（时间+地区城市+编号...） UUID
        orders.setNumber(UUID.randomUUID().toString().replace("-",""));
        //订单的状态：1: 待付款
        orders.setStatus(Orders.PENDING_PAYMENT);
        //1.2.3 设置user_id 用户ID
        orders.setUserId(BaseContext.getCurrentId());
        //1.2.4 设置address_book_id:: 页面有提供不用设置！
        //1.2.5 设置order_time:: 下单时间
        orders.setOrderTime(LocalDateTime.now());
        //1.2.6 设置checkout_time:: 付款时间，不知道，后续更新它
        //1.2.7 设置pay_method:: 支付方式，下单才知道。后续要更新它
        //1.2.8 设置pay_status:: 支付状态： 0 未支付， 1已支付， 2退款
        orders.setPayStatus(Orders.UN_PAID);
        //1.2.9 设置amount:: 订单总金额，页面提供
        //1.2.10 设置remark:: 备注，前端提供
        //1.2.11 设置phone::  收货人电话，需要设置
        orders.setPhone(addressBook.getPhone());
        //1.2.12 设置address :: 收货人地址，需要设置
        String address = addressBook.getProvinceName() + addressBook.getCityName() + addressBook.getDistrictName() + addressBook.getDetail();
        orders.setAddress(address);
        //1.2.13 设置user_name :: 下单的这个用户的昵称
        orders.setUserName(user.getName());
        //1.2.14 设置consignee :: 收货人姓名
        orders.setConsignee(addressBook.getConsignee());
        //1.2.15 设置cancel_reason :: 取消订单的原因，后续更新它
        //1.2.16 设置rejection_reason:: 拒绝订单的原因， 后续更新它
        //1.2.17 设置estimatedDeliveryTime :: 页面提供，不用设置
        //1.2.18 设置delivery_status:: 配送状态 页面提供，不用设置
        //1.2.18 设置delivery_time:: 送达时间，后续需要更新它
        //1.2.19 设置pack_amount :: 打包费，页面提供不用设置
        //1.2.20 设置tablewareNumber:: 餐具数量，页面提供，不用设置
        //1.2.21 设置tablewareStatus :: 餐具数量状态，页面提供，不用设置
    }

    /**
     * 获取经度和纬度坐标值
     *
     * @return
     */
    private Map<String, Double> getLngAndLatMap(Map<String, String> paramMap) {
        // 调用逆定理编码gc
        String json = HttpClientUtil.doGet(URL, paramMap);
        JSONObject jsonObject = JSON.parseObject(json);
        System.out.println(jsonObject);
        Map result = (Map) jsonObject.get("result");
        Map location = (Map) result.get("location");
        // 经度值
        BigDecimal lng = (BigDecimal) location.get("lng");
        double doubleLng = lng.setScale(6, RoundingMode.HALF_UP).doubleValue();
        // 纬度值
        BigDecimal lat = (BigDecimal) location.get("lat");
        double doubleLat = lat.setScale(6, RoundingMode.HALF_UP).doubleValue();
        // 构造返回参数
        Map<String, Double> map = new HashMap<>();
        map.put("lng", doubleLng);
        map.put("lat", doubleLat);
        return map;
    }

    /**
     * 校验配送距离
     *
     * @param shopLngAndLatMap 商家经纬度坐标
     * @param userLngAndLatMap 用户经纬度坐标
     */
    private boolean checkDistributionDistance(Map<String, Double> shopLngAndLatMap, Map<String, Double> userLngAndLatMap) {
        // 商家地址经度值
        Double shopLng = shopLngAndLatMap.get("lng");
        // 商家地址纬度值
        Double shopLat = shopLngAndLatMap.get("lat");
        // 用户地址经度值
        Double userLng = userLngAndLatMap.get("lng");
        // 用户地址纬度值
        Double userLat = userLngAndLatMap.get("lat");
        // 组装参数
        String origin = shopLat + "," + shopLng;
        String destination = userLat + "," + userLng;
        String json = HttpClientUtil.doGet(Riding_Address_Url, MapParamUtil.getRouteMap(origin, destination, shopProperties.getAk()));
        JSONObject jsonObject = JSON.parseObject(json);
        Integer status = (Integer) jsonObject.get("status");
        if (status == 2002) {
            // no riding path
            return true;
        }
        Map result = (Map) jsonObject.get("result");
        List routes = (List) result.get("routes");
        Map router = (Map) routes.get(0);
        // 判断距离的公里数是否大于5
        return (Double) router.get("distance") / 1000 > 5;
    }

    /**
     * 获取商家经纬度坐标map
     *
     * @return
     */
    private Map<String, Double> getShopLngAndLatMap() {
        return getLngAndLatMap(MapParamUtil.getAddressMap(shopProperties.getAddress(), "json", shopProperties.getAk()));
    }

    /**
     * 获取用户经纬度坐标map
     *
     * @param addressBook
     * @return
     */
    private Map<String, Double> getUserLngAndLatMap(AddressBook addressBook) {
        // 获取用户地址
        String address = addressBook.getCityName() + addressBook.getDistrictName() + addressBook.getDetail();
        return getLngAndLatMap(MapParamUtil.getAddressMap(address, "json", shopProperties.getAk()));
    }

    /**
     * 订单搜索
     * @param ordersPageQueryDTO
     * @return
     */
    @Override
    public PageResult conditionSearch(OrdersPageQueryDTO ordersPageQueryDTO) {
        //设置分页
        PageHelper.startPage(ordersPageQueryDTO.getPage(),ordersPageQueryDTO.getPageSize());

        Page<Orders> page = orderMapper.pageQuery(ordersPageQueryDTO);

        // 部分订单状态，需要额外返回订单菜品信息，将Orders转化为OrderVO
        List<OrderVO> orderVOList = getOrderVOList(page);

        return new PageResult(page.getTotal(),orderVOList);
    }
    private List<OrderVO> getOrderVOList(Page<Orders> page){
        // 需要返回订单菜品信息，自定义OrderVO响应结果
        List<OrderVO> orderVOList = new ArrayList<>();

        List<Orders> orders = page.getResult();
        for (Orders order : orders) {
            OrderVO orderVO = new OrderVO();
            BeanUtils.copyProperties(order,orderVO);
            String orderDishes = getOrderDishStr(order);

            // 将订单菜品信息封装到orderVO中，并添加到orderVOList
            orderVO.setOrderDishes(orderDishes);
            orderVOList.add(orderVO);
        }
        return orderVOList;
    }
    /**
     * 根据订单id获取菜品信息字符串
     * @param orders
     * @return
     */
    private String getOrderDishStr(Orders orders){
        // 查询订单菜品详情信息（订单中的菜品和数量）
        List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderId(orders.getId());
        // 将每一条订单菜品信息拼接为字符串（格式：宫保鸡丁*3；）
        List<String> orderDishList = orderDetailList.stream().map(orderDetail -> {
            String orderDish = orderDetail.getName() + "*" + orderDetail.getNumber() + ";";
            return orderDish;
        }).collect(Collectors.toList());
        // 将该订单对应的所有菜品信息拼接在一起
        return String.join("",orderDishList);
    }

    /**
     * 查看订单详细信息
     * @param id
     * @return
     */
    @Override
    public OrderVO details(Long id) {
        //根据id查询订单
        Orders orders = orderMapper.getById(id);

        //根据订单id获取订单详细信息
        List<OrderDetail> orderDetailList = orderDetailMapper.getByOrderId(orders.getId());

        OrderVO orderVO = new OrderVO();
        BeanUtils.copyProperties(orders,orderVO);
        orderVO.setOrderDetailList(orderDetailList);

        return orderVO;
    }

    /**
     * 取消订单
     * @param ordersCancelDTO
     */
    @Override
    public void cancel(OrdersCancelDTO ordersCancelDTO) {
        //根据id查询订单
        Orders orderDB = orderMapper.getById(ordersCancelDTO.getId());

        Orders orders = new Orders();
        orders.setId(orderDB.getId());
        orders.setStatus(Orders.CANCELLED);
        orders.setCancelReason(ordersCancelDTO.getCancelReason());
        orders.setCancelTime(LocalDateTime.now());

        orderMapper.update(orders);
    }

    /**
     * 订单派送
     * @param id
     */
    @Override
    public void delivery(Long id) {
        //根据id获得订单
        Orders orderDB = orderMapper.getById(id);

        //判断订单的状态
        if(orderDB == null || !orderDB.getStatus().equals(Orders.CONFIRMED)){
            throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
        }

        //修改订单状态
        Orders orders = new Orders();
        orders.setId(orderDB.getId());
        orders.setStatus(Orders.DELIVERY_IN_PROGRESS);

        orderMapper.update(orders);
    }


}
